home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 25 / AACD 25.iso / AACD / Magazine / Online / QMail / source / qmail-remote.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-02  |  13.2 KB  |  537 lines

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <netinet/in.h>
  4. #include <arpa/inet.h>
  5. #include "sig.h"
  6. #include "getln.h"
  7. #include "stralloc.h"
  8. #include "substdio.h"
  9. #include "subfd.h"
  10. #include "scan.h"
  11. #include "case.h"
  12. #include "error.h"
  13. #include "auto_qmail.h"
  14. #include "control.h"
  15. #include "dns.h"
  16. #include "alloc.h"
  17. #include "quote.h"
  18. #include "ip.h"
  19. #include "ipalloc.h"
  20. #include "ipme.h"
  21. #include "gen_alloc.h"
  22. #include "gen_allocdefs.h"
  23. #include "str.h"
  24. #include "now.h"
  25. #include "exit.h"
  26. #include "constmap.h"
  27. #include "tcpto.h"
  28. #include "timeoutconn.h"
  29. #include "timeoutread.h"
  30. #include "timeoutwrite.h"
  31.  
  32. #define HUGESMTPTEXT 5000
  33.  
  34. #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
  35. unsigned long port = PORT_SMTP;
  36.  
  37. GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
  38. GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
  39. static stralloc sauninit = {0};
  40.  
  41. stralloc helohost = {0};
  42. stralloc routes = {0};
  43. struct constmap maproutes;
  44. stralloc routesip = {0};
  45. struct constmap maproutesip;
  46. stralloc host = {0};
  47. stralloc sender = {0};
  48.  
  49. saa reciplist = {0};
  50.  
  51. struct ip_address partner;
  52.  
  53. void out(s) char *s; { if (substdio_puts(subfdout,s) == -1) _exit(0); }
  54. void zero() { if (substdio_put(subfdout,"\0",1) == -1) _exit(0); }
  55. void zerodie() { zero(); substdio_flush(subfdout); _exit(0); }
  56.  
  57. void outsafe(sa) stralloc *sa; { int i; char ch;
  58. for (i = 0;i < sa->len;++i) {
  59. ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?';
  60. if (substdio_put(subfdout,&ch,1) == -1) _exit(0); } }
  61.  
  62. void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
  63. void temp_oserr() { out("Z\
  64. System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
  65. void temp_noconn() { out("Z\
  66. Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); }
  67. void temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); }
  68. char *addrtype;
  69. void temp_dnscanon() { out("Z"); out(addrtype); out(" \
  70. CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); }
  71. void temp_dns() { out("Z\
  72. Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); }
  73. void temp_chdir() { out("Z\
  74. Unable to switch to home directory. (#4.3.0)\n"); zerodie(); }
  75. void temp_control() { out("Z\
  76. Unable to read control files. (#4.3.0)\n"); zerodie(); }
  77. void perm_partialline() { out("D\
  78. SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); }
  79. void perm_usage() { out("D\
  80. I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); }
  81. void perm_dns() { out("D\
  82. Sorry, I couldn't find any host named ");
  83. outsafe(&host);
  84. out(". (#5.1.2)\n"); zerodie(); }
  85. void perm_nomx() { out("D\
  86. Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
  87. zerodie(); }
  88. void perm_ambigmx() { out("D\
  89. Sorry. Although I'm listed as a best-preference MX or A for that host,\n\
  90. it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
  91. zerodie(); }
  92.  
  93. int timeout = 1200;
  94. int timeoutconnect = 60;
  95.  
  96. void getcontrols()
  97. {
  98.  int r;
  99.  if (control_init() == -1)
  100.   { if (errno == error_nomem) temp_nomem(); temp_control(); }
  101.  
  102.  if (control_readint(&timeout,"control/timeoutremote") == -1)
  103.   { if (errno == error_nomem) temp_nomem(); temp_control(); }
  104.  if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
  105.   { if (errno == error_nomem) temp_nomem(); temp_control(); }
  106.  
  107.  r = control_rldef(&helohost,"control/helohost",1,(char *) 0);
  108.  if (r == -1) if (errno == error_nomem) temp_nomem();
  109.  if (r != 1) temp_control();
  110.  
  111.  switch(control_readfile(&routes,"control/smtproutes",0))
  112.   {
  113.    case -1:
  114.      if (errno == error_nomem) temp_nomem(); temp_control();
  115.    case 0:
  116.      if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break;
  117.    case 1:
  118.      if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
  119.   }
  120.  
  121.  switch(control_readfile(&routesip,"control/smtproutesip",0))
  122.   {
  123.    case -1:
  124.      if (errno == error_nomem) temp_nomem(); temp_control();
  125.    case 0:
  126.      if (!constmap_init(&maproutesip,"",0,1)) temp_nomem(); break;
  127.    case 1:
  128.      if (!constmap_init(&maproutesip,routesip.s,routesip.len,1)) temp_nomem(); break;
  129.   }
  130. }
  131.  
  132. char smtptobuf[1024];
  133. char smtpfrombuf[128];
  134. stralloc smtpline = {0};
  135. stralloc smtptext = {0};
  136.  
  137. void outsmtptext()
  138. {
  139.  int i; 
  140.  if (smtptext.s) if (smtptext.len) if (smtptext.len < HUGESMTPTEXT)
  141.   {
  142.    if (substdio_puts(subfdout,"Remote host said: ") == -1) _exit(0);
  143.    for (i = 0;i < smtptext.len;++i)
  144.      if (!smtptext.s[i]) smtptext.s[i] = '?';
  145.    if (substdio_put(subfdout,smtptext.s,smtptext.len) == -1) _exit(0);
  146.    smtptext.len = 0;
  147.   }
  148. }
  149.  
  150. unsigned long smtpcode(ss)
  151. substdio *ss;
  152. {
  153.  int match;
  154.  unsigned long code;
  155.  
  156.  if (!stralloc_copys(&smtptext,"")) return 421;
  157.  do
  158.   {
  159.    if (getln(ss,&smtpline,&match,'\n') != 0) return 421;
  160.    if (!match) return 421;
  161.    if ((smtpline.len >= 2) && (smtpline.s[smtpline.len - 2] == '\r'))
  162.     {
  163.      smtpline.s[smtpline.len - 2] = '\n';
  164.      --smtpline.len;
  165.     }
  166.    if (!stralloc_cat(&smtptext,&smtpline)) return 421;
  167.    if (scan_nbblong(smtpline.s,smtpline.len,10,0,&code) != 3) return 421;
  168.    if (smtpline.len == 3) return code;
  169.   }
  170.  while (smtpline.s[3] == '-');
  171.  
  172.  return code;
  173. }
  174.  
  175. void outhost()
  176. {
  177.  char x[IPFMT];
  178.  
  179.  x[ip_fmt(x,&partner)] = 0;
  180.  out(x);
  181. }
  182.  
  183. void writeerr()
  184. {
  185.  out("ZConnected to "); outhost();
  186.  out(" but communications failed. (#4.4.2)\n");
  187.  zerodie();
  188. }
  189.  
  190. void quit(ssto,ssfrom)
  191. substdio *ssto;
  192. substdio *ssfrom;
  193. {
  194.  outsmtptext();
  195.  if (substdio_putsflush(ssto,"QUIT\r\n") != -1)
  196.    smtpcode(ssfrom); /* protocol design stupidity */
  197.  zerodie();
  198. }
  199.  
  200. stralloc dataline = {0};
  201.  
  202. void blast(ssto,ssfrom)
  203. substdio *ssto;
  204. substdio *ssfrom;
  205. {
  206.  int match;
  207.  
  208.  for (;;)
  209.   {
  210.    if (getln(ssfrom,&dataline,&match,'\n') != 0) temp_read();
  211.    if (!match && !dataline.len) break;
  212.    if (!match) perm_partialline();
  213.    --dataline.len;
  214.    if (dataline.len && (dataline.s[0] == '.'))
  215.      if (substdio_put(ssto,".",1) == -1) writeerr();
  216.    if (substdio_put(ssto,dataline.s,dataline.len) == -1) writeerr();
  217.    if (substdio_put(ssto,"\r\n",2) == -1) writeerr();
  218.   }
  219.  if (substdio_put(ssto,".\r\n",3) == -1) writeerr();
  220.  if (substdio_flush(ssto) == -1) writeerr();
  221. }
  222.  
  223. stralloc recip = {0};
  224.  
  225. void smtp(fd)
  226. int fd;
  227. {
  228.  substdio ssto;
  229.  substdio ssfrom;
  230.  unsigned long code;
  231.  int flaganyrecipok;
  232.  int i;
  233.  
  234.  substdio_fdbuf(&ssto,timeoutwrite,TIMEOUTWRITE(timeout,fd),smtptobuf,sizeof(smtptobuf));
  235.  substdio_fdbuf(&ssfrom,timeoutread,TIMEOUTREAD(timeout,fd),smtpfrombuf,sizeof(smtpfrombuf));
  236.  
  237.  if (smtpcode(&ssfrom) != 220)
  238.   {
  239.    out("ZConnected to "); outhost(); out(" but greeting failed.\n");
  240.    quit(&ssto,&ssfrom);
  241.   }
  242.  
  243.  if (substdio_puts(&ssto,"HELO ") == -1) writeerr();
  244.  if (substdio_put(&ssto,helohost.s,helohost.len) == -1) writeerr();
  245.  if (substdio_puts(&ssto,"\r\n") == -1) writeerr();
  246.  if (substdio_flush(&ssto) == -1) writeerr();
  247.  
  248.  if (smtpcode(&ssfrom) != 250)
  249.   {
  250.    out("ZConnected to "); outhost(); out(" but my name was rejected.\n");
  251.    quit(&ssto,&ssfrom);
  252.   }
  253.  
  254.  if (substdio_puts(&ssto,"MAIL FROM:<") == -1) writeerr();
  255.  if (substdio_put(&ssto,sender.s,sender.len) == -1) writeerr();
  256.  if (substdio_puts(&ssto,">\r\n") == -1) writeerr();
  257.  if (substdio_flush(&ssto) == -1) writeerr();
  258.  
  259.  code = smtpcode(&ssfrom);
  260.  if (code >= 500)
  261.   {
  262.    out("DConnected to "); outhost(); out(" but sender was rejected.\n");
  263.    quit(&ssto,&ssfrom);
  264.   }
  265.  if (code >= 400)
  266.   {
  267.    out("ZConnected to "); outhost(); out(" but sender was rejected.\n");
  268.    quit(&ssto,&ssfrom);
  269.   }
  270.  
  271.  flaganyrecipok = 0;
  272.  for (i = 0;i < reciplist.len;++i)
  273.   {
  274.    if (substdio_puts(&ssto,"RCPT TO:<") == -1) writeerr();
  275.    if (substdio_put(&ssto,reciplist.sa[i].s,reciplist.sa[i].len) == -1) writeerr();
  276.    if (substdio_puts(&ssto,">\r\n") == -1) writeerr();
  277.    if (substdio_flush(&ssto) == -1) writeerr();
  278.  
  279.    code = smtpcode(&ssfrom);
  280.    if (code == 421)
  281.     {
  282.      out("ZConnected to "); outhost(); out(" but connection died.\n");
  283.      quit(&ssto,&ssfrom);
  284.     }
  285.    if (code >= 500)
  286.     {
  287.      out("h"); outhost(); out(" does not like recipient.\n");
  288.      outsmtptext(); zero();
  289.     }
  290.    else if (code >= 400)
  291.     {
  292.      out("s"); outhost(); out(" does not like recipient.\n");
  293.      outsmtptext(); zero();
  294.     }
  295.    else
  296.     {
  297.      out("r"); zero();
  298.      flaganyrecipok = 1;
  299.     }
  300.   }
  301.  
  302.  if (!flaganyrecipok)
  303.   {
  304.    out("DGiving up.\n");
  305.    quit(&ssto,&ssfrom);
  306.   }
  307.  
  308.  if (substdio_putsflush(&ssto,"DATA\r\n") == -1) writeerr();
  309.  
  310.  code = smtpcode(&ssfrom);
  311.  if (code == 421)
  312.   {
  313.    out("ZConnected to "); outhost(); out(" but connection died.\n");
  314.    quit(&ssto,&ssfrom);
  315.   }
  316.  if (code >= 500)
  317.   {
  318.    out("D"); outhost(); out(" failed on DATA command.\n");
  319.    quit(&ssto,&ssfrom);
  320.   }
  321.  if (code >= 400)
  322.   {
  323.    out("Z"); outhost(); out(" failed on DATA command.\n");
  324.    quit(&ssto,&ssfrom);
  325.   }
  326.  
  327.  blast(&ssto,subfdin);
  328.  
  329.  code = smtpcode(&ssfrom);
  330.  if (code == 421)
  331.   {
  332.    out("ZConnected to "); outhost(); out(" but connection died. Possible duplicate!\n");
  333.    quit(&ssto,&ssfrom);
  334.   }
  335.  if (code >= 500)
  336.   {
  337.    out("D"); outhost(); out(" failed after I sent the message.\n");
  338.    quit(&ssto,&ssfrom);
  339.   }
  340.  if (code >= 400)
  341.   {
  342.    out("Z"); outhost(); out(" failed after I sent the message.\n");
  343.    quit(&ssto,&ssfrom);
  344.   }
  345.  
  346.  out("K"); outhost(); out(" accepted message.\n");
  347.  quit(&ssto,&ssfrom);
  348. }
  349.  
  350. stralloc canonhost = {0};
  351. stralloc canonbox = {0};
  352.  
  353. void addrmangle(saout,s,flagalias,flagcname)
  354. stralloc *saout; /* host has to be canonical, box has to be quoted */
  355. char *s;
  356. int *flagalias;
  357. int flagcname;
  358. {
  359.  int j;
  360.  
  361.  *flagalias = flagcname;
  362.  
  363.  j = str_rchr(s,'@');
  364.  if (!s[j])
  365.   {
  366.    if (!stralloc_copys(saout,s)) temp_nomem();
  367.    return;
  368.   }
  369.  if (!stralloc_copys(&canonbox,s)) temp_nomem();
  370.  canonbox.len = j;
  371.  if (!quote(saout,&canonbox)) temp_nomem();
  372.  if (!stralloc_cats(saout,"@")) temp_nomem();
  373.  
  374.  if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
  375.  if (flagcname)
  376.    switch(dns_cname(&canonhost))
  377.     {
  378.      case 0: *flagalias = 0; break;
  379.      case DNS_MEM: temp_nomem();
  380.      case DNS_SOFT: temp_dnscanon();
  381.      case DNS_HARD: ; /* alias loop, not our problem */
  382.     }
  383.  
  384.  if (!stralloc_cat(saout,&canonhost)) temp_nomem();
  385. }
  386.  
  387. void main(argc,argv)
  388. int argc;
  389. char **argv;
  390. {
  391.  static ipalloc ip = {0};
  392.  int i;
  393.  unsigned long random;
  394.  char **recips;
  395.  unsigned long prefme;
  396.  int flagallaliases;
  397.  int flagalias;
  398.  char *relayhost;
  399.  
  400.  sig_pipeignore();
  401.  if (argc < 4) perm_usage();
  402.  if (chdir(auto_qmail) == -1) temp_chdir();
  403.  getcontrols();
  404.  
  405.  
  406.  if (!stralloc_copys(&host,argv[1])) temp_nomem();
  407.  
  408.  relayhost = 0;
  409.  for (i = 0;i <= host.len;++i)
  410.    if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
  411.      if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
  412.        break;
  413.  if (relayhost && !*relayhost) relayhost = 0;
  414.  
  415.  if (relayhost)
  416.   {
  417.    i = str_chr(relayhost,':');
  418.    if (relayhost[i])
  419.     {
  420.      scan_ulong(relayhost + i + 1,&port);
  421.      relayhost[i] = 0;
  422.     }
  423.    if (!stralloc_copys(&host,relayhost)) temp_nomem();
  424.   }
  425.  
  426.  
  427.  addrtype = "Sender";
  428.  addrmangle(&sender,argv[2],&flagalias,!relayhost);
  429.  
  430.  if (!saa_readyplus(&reciplist,0)) temp_nomem();
  431.  if (ipme_init() != 1) temp_oserr();
  432.  
  433.  addrtype = "Recipient";
  434.  flagallaliases = 1;
  435.  recips = argv + 3;
  436.  while (*recips)
  437.   {
  438.    if (!saa_readyplus(&reciplist,1)) temp_nomem();
  439.    reciplist.sa[reciplist.len] = sauninit;
  440.    addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost);
  441.    if (!flagalias) flagallaliases = 0;
  442.    ++reciplist.len;
  443.    ++recips;
  444.   }
  445.  
  446.  
  447.  random = now() + (getpid() << 16);
  448.  switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random))
  449.   {
  450.    case DNS_MEM: temp_nomem();
  451.    case DNS_SOFT: temp_dns();
  452.    case DNS_HARD: perm_dns();
  453.    case 1:
  454.      if (ip.len <= 0) temp_dns();
  455.   }
  456.  
  457.  if (ip.len <= 0) perm_nomx();
  458.  
  459.  prefme = 100000;
  460.  for (i = 0;i < ip.len;++i)
  461.    if (ipme_is(&ip.ix[i].ip))
  462.      if (ip.ix[i].pref < prefme)
  463.        prefme = ip.ix[i].pref;
  464.  
  465.  if (relayhost) prefme = 300000;
  466.  if (flagallaliases) prefme = 500000;
  467.  
  468.  for (i = 0;i < ip.len;++i)
  469.    if (ip.ix[i].pref < prefme)
  470.      break;
  471.  
  472.  if (i >= ip.len)
  473.    perm_ambigmx();
  474.  
  475.  for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme)
  476.   {
  477.    int s;
  478.  
  479.    /* Map IP numbers according to control/smtproutesip. */
  480.    unsigned int j;
  481.    int len;
  482.    char hostip[16]; /* Enough to hold xxx.xxx.xxx.xxx */
  483.    ipalloc ip2 = {0};
  484.    stralloc host2 = {0};
  485.    char *relayhost2 = 0;
  486.    unsigned long connport = port, port2 = port;
  487.  
  488.    hostip[len = ip_fmt (hostip,&ip.ix[i].ip)] = '\0';
  489.  
  490.    for (j = len; (signed int) j > -1; j --)
  491.      if ((j == len) || (j == 0) || (hostip[j - 1] == '.'))
  492.        if ((relayhost2 = constmap (&maproutesip, hostip, j)))
  493.          break;
  494.    if (relayhost2 && !*relayhost2) relayhost2 = 0;
  495.  
  496.    if (relayhost2)
  497.    {
  498.      j = str_chr(relayhost2,':');
  499.      if (relayhost2[j])
  500.       {
  501.        scan_ulong(relayhost2 + j + 1,&port2);
  502.        relayhost2[j] = 0;
  503.       }
  504.      if (!stralloc_copys(&host2,relayhost2)) temp_nomem();
  505.  
  506.      switch (dns_ip (&ip2, &host2))
  507.      {
  508.        case DNS_HARD: break;
  509.        case DNS_SOFT: temp_dns ();
  510.        case DNS_MEM:  temp_nomem ();
  511.        case 1:
  512.          if (ip2.len <= 0) temp_dns();
  513.      }
  514.  
  515.      ip.ix[i].ip = ip2.ix[0].ip;
  516.      connport = port2;
  517.      if (!stralloc_copy(&host,&host2)) temp_nomem ();
  518.    }
  519.  
  520.    if (tcpto(&ip.ix[i].ip)) continue;
  521.  
  522.    s = socket(AF_INET,SOCK_STREAM,0);
  523.    if (s == -1) temp_oserr();
  524.  
  525.    if (timeoutconn(s,&ip.ix[i].ip, connport,timeoutconnect) == 0)
  526.     {
  527.      tcpto_err(&ip.ix[i].ip,0);
  528.      partner = ip.ix[i].ip;
  529.      smtp(s); /* does not return */
  530.     }
  531.    tcpto_err(&ip.ix[i].ip,errno == error_timeout);
  532.    close(s);
  533.   }
  534.  
  535.  temp_noconn();
  536. }
  537.